package control;

import java.text.DecimalFormat;
import java.util.Calendar;

import javax.swing.JOptionPane;
import javax.swing.JTextField;

import utils.DataFormat;

import basic.Date;

import com.toedter.calendar.JDateChooser;

//@author A0097788Y
public class ValidateInput {

	public static final int ERR_HAS_COMMA = 1;
	public static final int ERR_NO_INPUT = 2;
	public static final int ERR_INVALID_INPUT = 3;
	public static final int ERR_NEGATIVE = 4;
	public static final int ERR_OUT_OF_RANGE = 5;
	public static final int ERR_TOO_LONG = 6;
	public static final int ERR_DATE_IMPOSSIBLE = 7;
	public static final int ERR_DUEDATE_SOON = 8;
	public static final int ERR_FUTURE_DATE = 9;
	public static final int ERR_MAX_YEARS = 10;

	/**
	 * checks if 
	 * @param input
	 * @return 0 if valid
	 * @Precondition input must not be empty or contain ','
	 * @Postcondition 
	 */
	public static int validateName(String input) 
	{
		if (hasComma(input))
		{
			return ERR_HAS_COMMA;												//error 1:has comma
		}
		if (input.trim().isEmpty())
		{
			return ERR_NO_INPUT;
		}																		//error 2:no input
		if(input.length()>32)
		{
			return ERR_TOO_LONG;												//error 6:name too long
		}
		return 0;
	}

	/**
	 * 
	 * @param input
	 * @return o if valid
	 * @Precondition 
	 * @Postcondition 
	 */
	public static int validateAmount(String input) {
		if (hasComma(input))
			return ERR_HAS_COMMA;												//error 1:has comma
		if (input.trim().isEmpty())
			return ERR_NO_INPUT;												//error 2:no input
		try{
			double amount = Double.parseDouble(input);							//error 3: invalid input, not a double
			if (amount<0)
				return ERR_NEGATIVE;											//error 4: negative input
			DecimalFormat f = new DecimalFormat("#.##");
			if (Double.parseDouble(f.format(amount))!=amount)
				return ERR_OUT_OF_RANGE;										//error 5: more than 2 decimal places
		}
		catch(NumberFormatException e){
			return ERR_INVALID_INPUT;												
		}
		return 0;
	}

	/**
	 * 
	 * @param input
	 * @return 0 if valid
	 * @Precondition Interest rate must be between 0 and 20%
	 * @Postcondition 
	 */
	public static int validateInterestRate(String input) {
		if (hasComma(input))
			return ERR_HAS_COMMA;												//error 1:has comma
		if (input.trim().isEmpty())
			return ERR_NO_INPUT;												//error 2:no input
		try{
			double amount = Double.parseDouble(input);							//error 3: invalid input, not a double
			if (amount<0)
				return ERR_NEGATIVE;											//error 4: negative input
			if (amount>20)
				return ERR_OUT_OF_RANGE;										//error 5: more than 20%
		}
		catch(NumberFormatException e){
			return ERR_INVALID_INPUT;												
		}
		return 0;
	}

	/**
	 * 
	 * @param calendar
	 * @return 0 if valid
	 * @Precondition 
	 * @Postcondition 
	 */
	public static int validateDate(JDateChooser calendar) {
		try{
			String input = ((JTextField)calendar.getDateEditor().getUiComponent()).getText();
			if (hasComma(input))
				return ERR_HAS_COMMA;														//error 1:has comma	
			if (input.trim().isEmpty())
				return ERR_NO_INPUT;														//error 2:no input
			for (int i=0; i<input.length();i++){
				if (!(input.charAt(i)=='/' || (input.charAt(i)>='0' && input.charAt(i)<='9'))){
					return ERR_INVALID_INPUT;												//error 3: invalid input, contains char that are not numbers or '/'
				}
			}
			calendar.getCalendar().get(Calendar.DAY_OF_MONTH);								//error 7: invalid date
			calendar.getCalendar().get(Calendar.MONTH);							
			calendar.getCalendar().get(Calendar.YEAR);							
			if (calendar.getCalendar().get(Calendar.YEAR)>=9999)							//error 6: year greater than/equal 9,999
				return ERR_TOO_LONG;		
			if(isFutureDate(calendar))
				return ERR_FUTURE_DATE;														//error 9: date is in the future
		}
		catch(NullPointerException e){
			return ERR_DATE_IMPOSSIBLE;
		}
		return 0;
	}

	/**
	 * 
	 * @param date
	 * @return 0 if valid
	 * @Precondition 
	 * @Postcondition 
	 */
	public static int validateDate(Date date) {
		try{
			Calendar calendar = Calendar.getInstance();
			calendar.set(Calendar.DATE, date.get_day());										//error 7: invalid date
			calendar.set(Calendar.MONTH, date.get_month()-1);							
			calendar.set(Calendar.YEAR, date.get_year());	
			if (date.get_day()!=calendar.get(Calendar.DAY_OF_MONTH))
				return ERR_DATE_IMPOSSIBLE;
			if (calendar.get(Calendar.YEAR)>=9999)												//error 6: year greater than/equal 9,999
				return ERR_TOO_LONG;		
			if(date.isGreaterThan(Date.getCurrentDate()))
				return ERR_FUTURE_DATE;															//error 9: date in the future
		}
		catch(NullPointerException e){
			return ERR_DATE_IMPOSSIBLE;
		}
		return 0;
	}

	/**
	 * 
	 * @param calendar
	 * @return 0 if dueDate is greater than startDate by 1 month
	 * @Precondition 
	 * @Postcondition 
	 */
	public static int validateDateOrder(JDateChooser startDate, JDateChooser dueDate) {
		Calendar start = startDate.getCalendar();
		Calendar due = dueDate.getCalendar();
		start.roll(Calendar.MONTH, true);
		if (start.compareTo(due)>0)
			return ERR_DUEDATE_SOON;											//error 8: due date less than 1 month ahead
		if(due.YEAR - 100 > start.YEAR)
			return ERR_MAX_YEARS;
		
		return 0;
	}

	/**
	 * 
	 * @param input
	 * @return 0 if valid
	 * @Precondition 
	 * @Postcondition 
	 */
	public static int validateDescription(String input) {
		if (hasComma(input))
			return ERR_HAS_COMMA;											//error 1:has comma	
		if(input.length()>512)
			return ERR_TOO_LONG;											//error 6:too long	
		return 0;

	}

	/**
	 * 
	 * @param input
	 * @return 0 if valid
	 * @Precondition 
	 * @Postcondition 
	 */
	public static int validatePartyInvolved(String input) {
		if (hasComma(input))
			return ERR_HAS_COMMA;											//error 1:has comma
		if(input.length()>32)
			return ERR_TOO_LONG;											//error 6:too long
		return 0;
	}

	/**
	 * 
	 * @param input
	 * @return 0 if valid
	 * @Precondition 
	 * @Postcondition 
	 */
	public static int validateYear(String input) {
		if (hasComma(input))
			return ERR_HAS_COMMA;												//error 1:has comma
		if (input.trim().isEmpty())
			return ERR_NO_INPUT;												//error 2:no input
		try{
			int amount = Integer.parseInt(input);								//error 3: invalid input, not a int
			if (amount<0)
				return ERR_NEGATIVE;											//error 4: negative input
			if(amount > 100)
				return ERR_MAX_YEARS;											//error 10: years to invest is too far into the future
		}
		catch(NumberFormatException e){
			return ERR_INVALID_INPUT;												
		}
		
		return 0;
	}
	
	
	/**
	 * 
	 * @param input
	 * @param balance
	 * @return true if input > balance
	 * @Precondition String input has already been checked by validateAmount
	 * @Postcondition 
	 */
	private static boolean checkAffordable(String input, double balance) {
		double inputValue = Double.parseDouble(input);
		
		try {
		String balanceString = DataFormat.returnTwoDecimals(balance);
		balanceString = balanceString.replaceAll(",","");
		double balanceValue = Double.parseDouble(balanceString);
		if (inputValue > balanceValue) {
			return false;
		}
		}
		catch(Exception e) {
			return false;
		}

		return true;
	}
	
	/**
	 * 
	 * @param input
	 * @return true if ',' is detected
	 * @Precondition 
	 * @Postcondition 
	 */
	private static boolean hasComma(String input){
		if(input.contains(",")==true)
			return true;
		else
			return false;
	}

	/**
	 * 
	 * @param calendar
	 * @return true if selected date in calendar is ahead of the current date
	 * @Precondition 
	 * @Postcondition 
	 */
	private static boolean isFutureDate(JDateChooser calendar)
	{
		int day = calendar.getCalendar().get(Calendar.DATE);
		int month = calendar.getCalendar().get(Calendar.MONTH)+1;
		int year = calendar.getCalendar().get(Calendar.YEAR);
		Date startDate = new Date(day,month,year);
		if(startDate.isGreaterThan(Date.getCurrentDate()))
			return true;
		return false;
	}
	


	/**
	 * 
	 * @param text
	 * @return true if valid
	 * @Precondition 
	 * @Postcondition 
	 */
	public static boolean validName(String text){
		switch (validateName(text)){
		case ERR_HAS_COMMA:
			JOptionPane.showMessageDialog(null, "Name: Inputs cannot contain a comma!");
			return false;
		case ERR_NO_INPUT:
			JOptionPane.showMessageDialog(null, "Name: No input detected!");
			return false;
		case ERR_TOO_LONG:
			JOptionPane.showMessageDialog(null, "Name: Too long!");
			return false;

		}
		return true;
	}

	/**
	 * 
	 * @param text
	 * @param name
	 * @return true if valid
	 * @Precondition 
	 * @Postcondition 
	 */
	public static boolean validPartyInvolved(String text, String name){
		switch (validatePartyInvolved(text)){
		case ERR_HAS_COMMA:
			JOptionPane.showMessageDialog(null, name + ": Inputs cannot contain a comma!");
			return false;
		case ERR_TOO_LONG:
			JOptionPane.showMessageDialog(null, name + ": Too long!");
			return false;
		}
		return true;
	}

	/**
	 * 
	 * @param text
	 * @param name
	 * @return true if valid
	 * @Precondition 
	 * @Postcondition 
	 */
	public static boolean validDescription(String text, String name){
		switch (validateDescription(text)){
		case ERR_HAS_COMMA:
			JOptionPane.showMessageDialog(null, name + ": Inputs cannot contain a comma!");
			return false;
		case ERR_TOO_LONG:
			JOptionPane.showMessageDialog(null, name + ": Too long!");
			return false;
		}
		return true;
	}

	/**
	 * 
	 * @param text
	 * @return true if valid
	 * @Precondition 
	 * @Postcondition 
	 */
	public static boolean validAmount(String text){
		switch (validateAmount(text)){
		case ERR_HAS_COMMA:
			JOptionPane.showMessageDialog(null, "Amount: Inputs cannot contain a comma!");
			return false;
		case ERR_NO_INPUT:
			JOptionPane.showMessageDialog(null, "Amount: No input detected!");
			return false;
		case ERR_INVALID_INPUT:
			JOptionPane.showMessageDialog(null, "Amount: Invalid input detected!");
			return false;
		case ERR_NEGATIVE:
			JOptionPane.showMessageDialog(null, "Amount: Input should not be negative!");
			return false;
		case ERR_OUT_OF_RANGE:
			JOptionPane.showMessageDialog(null, "Amount: Input should not have more than 2 decimal places!");
			return false;
		}
		return true;
	}

	/**
	 * 
	 * @param calendar
	 * @param name
	 * @return true if valid
	 * @Precondition 
	 * @Postcondition 
	 */
	public static boolean validDate(JDateChooser calendar, String name){
		switch (validateDate(calendar)){
		case ERR_HAS_COMMA:
			JOptionPane.showMessageDialog(null, name + ": Inputs cannot contain a comma!");
			return false;
		case ERR_INVALID_INPUT:
			JOptionPane.showMessageDialog(null, name + ": Invalid input detected!");
			return false;
		case ERR_FUTURE_DATE:
			JOptionPane.showMessageDialog(null, name + ": No future date!");
			return false;
		case ERR_DATE_IMPOSSIBLE:
			JOptionPane.showMessageDialog(null, name + ": Invalid date detected!");
			return false;
		case ERR_TOO_LONG:
			JOptionPane.showMessageDialog(null, name + ": Year exceeds suitable range!");
			return false;
		}
		return true;
	}

	/**
	 * 
	 * @param calendar
	 * @param name
	 * @return  true if valid
	 * @Precondition 
	 * @Postcondition 
	 */
	public static boolean validMandatoryDate(JDateChooser calendar, String name){
		switch (validateDate(calendar)){
		case ERR_HAS_COMMA:
			JOptionPane.showMessageDialog(null, name + ": Inputs cannot contain a comma!");
			return false;
		case ERR_NO_INPUT:
			JOptionPane.showMessageDialog(null, name + ": No input detected!");
			return false;
		case ERR_INVALID_INPUT:
			JOptionPane.showMessageDialog(null, name + ": Invalid input detected!");
			return false;
		case ERR_DATE_IMPOSSIBLE:
			JOptionPane.showMessageDialog(null, name + ": Invalid date detected!");
			return false;
		case ERR_TOO_LONG:
			JOptionPane.showMessageDialog(null, name + ": Year exceeds suitable range!");
			return false;
		}
		return true;
	}

	/**
	 * 
	 * @param startDate
	 * @param dueDate
	 * @return true if valid
	 * @Precondition both dates have been checked via validDate
	 * @Postcondition 
	 */
	public static boolean validDateOrder(JDateChooser startDate, JDateChooser dueDate) {
		if (isFutureDate(startDate)) {
			JOptionPane.showMessageDialog(null, "Date : Start date cannot be in the future!");
			return false;
		}
		switch(validateDateOrder(startDate, dueDate)) {
		case ERR_DUEDATE_SOON:
			JOptionPane.showMessageDialog(null, "Date : Loan Due Date must be at least one month after Start Date!");
			return false;
		case ERR_MAX_YEARS:
			JOptionPane.showMessageDialog(null, "Date : Loan Due Date cannot be more than 100 years after Start Date!");
			return false;
		}
		return true;
	}
	
	//@author A0103947A
	/**
	 * 
	 * @param text
	 * @param field
	 * @param balance
	 * @param operator
	 * @return true if valid
	 * @Precondition operator contains name of section e.g. "Savings Analysis", field contains the name of field
	 * @Postcondition 
	 */
	public static boolean validInvestmentAmount(String text, String field, double balance, String operator) {
		switch (validateAmount(text)){
		case ERR_HAS_COMMA:
			JOptionPane.showMessageDialog(null, operator + ": " + field + "input cannot contain a comma!");
			return false;
		case ERR_NO_INPUT:
			JOptionPane.showMessageDialog(null, operator + ": No " + field + "input detected!");
			return false;
		case ERR_INVALID_INPUT:
			JOptionPane.showMessageDialog(null,  operator + ": Invalid " + field + "input detected!");
			return false;
		case ERR_NEGATIVE:
			JOptionPane.showMessageDialog(null,  operator + ": " + field +"input should not be negative!");
			return false;
		case ERR_OUT_OF_RANGE:
			JOptionPane.showMessageDialog(null, operator + ": " + field + "input should not have more than 2 decimal places!");
			return false;
		}
		
		if (!checkAffordable(text, balance)) {
			if (operator == "Payment Analysis") {
				JOptionPane.showMessageDialog(null, operator + ": Purchase amount cannot be greater than current balance!");
				return false;
			}
			else { //(operator == "Savings Analysis")
				JOptionPane.showMessageDialog(null, operator + ": Invested amount cannot be greater than current balance!");
				return false;
			}
		}
		
		return true;
	}
	
	/**
	 * 
	 * @param input
	 * @param yearField
	 * @return true if valid
	 * @Precondition yearField contains the name of field
	 * @Postcondition 
	 */
	public static boolean validYears(String input, String yearField) {
		switch(validateYear(input)) {
		case ERR_HAS_COMMA:
			JOptionPane.showMessageDialog(null, "Savings Analysis: " + yearField + " year input cannot contain a comma!");
			return false;
		case ERR_NO_INPUT:
			JOptionPane.showMessageDialog(null, "Savings Analysis: No input detected for " + yearField + " input year!");
			return false;
		case ERR_INVALID_INPUT:
			JOptionPane.showMessageDialog(null,  "Savings Analysis: Invalid input detected for " + yearField + " input year!");
			return false;
		case ERR_NEGATIVE:
			JOptionPane.showMessageDialog(null,  "Savings Analysis: " + yearField + " year input should not be negative!");
			return false;
		case ERR_MAX_YEARS:
			JOptionPane.showMessageDialog(null, "Savings Analysis: " + yearField + " year input cannot exceed 100 yaers!");
			return false;
		}
		return true;
	}

	/**
	 * 
	 * @param input
	 * @param panel
	 * @return true if valid
	 * @Precondition panel contains the name of panel
	 * @Postcondition 
	 */
	public static boolean validInterestRate(String input, String panel) {
		switch(validateInterestRate(input)) {
		case ERR_HAS_COMMA:
			JOptionPane.showMessageDialog(null, panel + ": Interest rate cannot contain a comma!");
			return false;
		case ERR_NO_INPUT:
			JOptionPane.showMessageDialog(null, panel + ": No input detected for interest rate!");
			return false;
		case ERR_NEGATIVE:
			JOptionPane.showMessageDialog(null, panel + ": Interest rate should not be negative!");
			return false;
		case ERR_OUT_OF_RANGE:
			JOptionPane.showMessageDialog(null, panel + ": Interest rate cannot exceed 20%!");
			return false;
		case ERR_INVALID_INPUT:
			JOptionPane.showMessageDialog(null, panel + ": Invalid input detected for interest rate!");
			return false;
		}
		return true;
	}
}
